package psd;

import java.io.*;
import java.util.*;
import java.text.*;


public class RegParser {

   public static final String HEADER_STRING_IND = "first,last,pubname,email,add_to_list,man,woman,GQ,age,lookM,lookW,lookGQ,minAge,maxAge,lookGroup,reffered,friends,location1,fromEB,fromSB,fromNB,fromSF,fromSE\n";
   public static final String HEADER_STRING_GROUP = "first1,last1,first2,last2,first3,last3,first4,last4,pubname,email,add_to_list,man,woman,GQ,ageYoung,ageOld,lookM,lookW,lookGQ,minAge,maxAge,lookGroup,referred,friends,location1,fromEB,fromSB,fromSF,fromNB,fromSE\n";
   
  File outputIndFile = null;
  File outputGroupFile = null;
  
  Writer outputIndWriter = null;
  Writer outputGroupWriter = null;
  
  String outputIndFileName = "outputIndiv.csv";
  String outputGroupFileName = "outputGroup.csv";
  
  boolean outputError = false;
  
  static public void main(String [] args) {
    RegParser parser = new RegParser();
    parser.parse(args);
  }

   void setupWriters( ) {
    try {
      
        outputIndFile = new File(outputIndFileName);
        
        if (outputIndFile.canWrite()) {
          System.err.println("Unable to write output file, bailing out: " + outputIndFileName);
          outputError = true;
          return;
        }
        
        outputIndWriter = new PrintWriter(outputIndFile);

        outputIndWriter.write( HEADER_STRING_IND );
        
    } catch (IOException exc) {
      System.err.println("Output file IOException, bailing out: " + outputIndFileName);
      exc.printStackTrace(System.err);
      outputError = true;
      return;
    }

    try {
      
        outputGroupFile = new File(outputGroupFileName);
        
        if (outputGroupFile.canWrite()) {
          System.err.println("Unable to write output file, bailing out: " + outputGroupFileName);
          outputError = true;
          return;
        }
        
        outputGroupWriter = new PrintWriter(outputGroupFile);

        outputGroupWriter.write( HEADER_STRING_GROUP );
    } catch (IOException exc) {
      System.err.println("Output file IOException, bailing out: " + outputGroupFileName);
      exc.printStackTrace(System.err);
      outputError = true;
      return;
    }
    
   }

   void closeWriters( ) {
          if (outputIndWriter != null) {
      try {
        outputIndWriter.close();
      } catch (IOException exc) {}
    }
      
    if (outputGroupWriter != null) {
      try {
        outputGroupWriter.close();
      } catch (IOException exc) {}
    }

   }
   
  public void parse(String [] args) {
    
    if (args == null || args.length == 0) {
      System.err.println("usage: java RegParser [-outputIndiv <output file>] " +
          "[-outputGroup <output file>] <file-or-directory>");
      return;
    }
    
    ArrayList<String> stringDirs = new ArrayList<String>();
    
    for (int i = 0; i < args.length; i++) {
      String arg = args[i];
      
      if (arg == null) continue;
      
      if (arg.equals("-outputIndiv")) {
        
        if (i + 1 >= args.length || args[i + 1] == null) {
          System.err.println("error: -outputIndiv must be followed by output filename");
        } else {
          outputIndFileName = args[i + 1];
          i++;
        }
        continue;
      } 
      
      if (arg.equals("-outputGroup")) {
        
        if (i + 1 >= args.length || args[i + 1] == null) {
          System.err.println("error: -outputGroup must be followed by output filename");
        } else {
          outputGroupFileName = args[i + 1];
          i++;
        }
        continue;
      }
      
      // otherwise, it is just a file or directory name
      stringDirs.add(args[i]);
    }

    setupWriters();
       
    for (int i = 0; i < stringDirs.size(); i++) {
      String dir = stringDirs.get(i);
      File dirFile = new File(dir);
      if (!dirFile.exists())
        System.err.println("File or directory does not exist: " + dir);
      else
        parseDir(dirFile);
      
      if (outputError) return;
    }

    closeWriters();
  }
  
  void parseDir (File directory) {
    
    if (!directory.exists()) return;
    
    if (directory.isFile())
      parseFile(directory);
    else {
      
      File [] fileChildren = directory.listFiles();
      if (fileChildren != null)
        for (int i = 0; i < fileChildren.length; i++) {
          parseDir(fileChildren[i]);
          if (outputError) return;
        }
    }
  }
  
  void parseFile (File file) {
    
    if (!file.exists() || !file.isFile()) return;
    
    if (!file.getName().endsWith(".txt")) {
      System.err.println("Skipping file because it does not have .txt extension: " + file.getName());
      return;
    }
    
    // read into String
    String fileString = null;
    InputStream fileStream = null;
    try {
      fileStream = new FileInputStream(file);
      
      byte [] buf = new byte [11000];
      
      int bytesRead = fileStream.read(buf);
      if (bytesRead > 10000) {
        System.err.println("Skipping file because it is too large: " + file.getName());
        return;
      }

      if (bytesRead < 10) {
        System.err.println("Skipping file because it is too small: " + file.getName());
        return;
      }

      fileString = new String(buf, 0, bytesRead);
      
    } catch (IOException exc) {
      System.err.println("Skipping file due to IOException: " + file.getName());
      exc.printStackTrace(System.err);
      return;
    } finally {
      try {
        if (fileStream != null) fileStream.close();
      } catch (IOException exc) {}
    }
    
    if (fileString == null) return;
    
    // tokenize by return characters
    StringTokenizer tokenizer = new StringTokenizer(fileString, "\n");
    ArrayList<String> fileLines = new ArrayList<String>();
    while (tokenizer.hasMoreTokens())
      fileLines.add(tokenizer.nextToken());
    
    if (fileLines.size() == 0) {
      System.err.println("Skipping file because it has no lines: " + file.getName());
      return;      
    }
    
    // check header
    String header = fileLines.get(0);
    if (!header.equals("#POLY SPEED DATE RESULTS FILE")) {
      System.err.println("Skipping file because it does not start with proper header line: " + file.getName());
      return;            
    }
    fileLines.remove(0);
    
    // check group vs. indiv
    boolean indiv = true;
    while (fileLines.size() > 0) {
      String tail = fileLines.get(fileLines.size() - 1);
      
      fileLines.remove(fileLines.size() - 1);
      
      if (tail == null || tail.trim().length() == 0)        
        continue;

      if (tail.trim().equals("Submit_Indiv: Submit_Indiv")) {
        indiv = true;
        break;
      }
      
      if (tail.trim().equals("Submit_Group: Submit_Group")) {
        indiv = false;
        break;
      }
      
      System.err.println("Skipping file because it does not end with group or individual tail line: " + file.getName());
      return;
    }
    
    if (indiv) 
      parseIndividualFile(fileLines, file.getName());
    else
      parseGroupFile(fileLines, file.getName());
    
  }
  
  void parseIndividualFile(ArrayList<String> fileLines, String fileName) {
    
    GroupReg reg = new GroupReg( false );
    
    for (int i = 0; i < fileLines.size(); i++) {
 
    	String fileLine = fileLines.get(i);
      
      int indexOfColon = fileLine.indexOf(": ");
      if (indexOfColon < 0) {
        printLineErr(fileLine, fileName);
        continue;
      }
      
      String left = fileLine.substring(0, indexOfColon);
      String right = fileLine.substring(indexOfColon + 2);
      
      // we may have multiple lines for friends
      //      System.out.println("$$$ 1");
      if (left.equals("friends")) {
         //        System.out.println("$$$ 2");
        int offset = 0;
        while (fileLines.size() > i + offset + 1) {
          String next = fileLines.get(i + offset + 1);
          if (next.indexOf(": ") < 0) {
            right += " " + next;
            offset++;
          } else
            break;
        }
        //        System.out.println("$$$ 3" + offset + " " + right);

        i += offset;
      }
      
      processLine( reg, fileLine, fileName, left, right );
      
    
    }
    
    try {
       
       outputIndWriter.write( reg + "\n" );
      
    } catch (IOException exc) {
      System.err.println("Output file IOException, bailing out: " + outputIndFileName);
      exc.printStackTrace(System.err);
      outputError = true;
      return;
    }


   
  }
  
    
  private void processLine(GroupReg reg, String fileLine, String fileName, String left, String right) {
	
	  if (left.equals("first")) {
	        reg.first1 = right;
	      } else if (left.equals("last")) {
	        reg.last1 = right;
	      } else if (left.equals("pubname")) {
	        reg.pubname = right;
	      } else if (left.equals("email")) {
	        reg.email = right;
	      } else if (left.equals("add_to_list")) {
	        Boolean val = parseBoolean(right);
	        if (val == null)
	          printLineErr(fileLine, fileName);
	        else
	          reg.add_to_list = val.booleanValue();
	      } else if (left.equals("man")) {
	        if (!right.equals("isMan"))
	          printLineErr(fileLine, fileName);
	        else
	          reg.man = true;
	      } else if (left.equals("woman")) {
	        if (!right.equals("isWoman"))
	          printLineErr(fileLine, fileName);
	        else
	          reg.woman = true;
	      } else if (left.equals("genderqueer")) {
	        if (!right.equals("isGQ"))
	          printLineErr(fileLine, fileName);
	        else
	          reg.GQ = true;
	      } else if (left.equals("age")) {
	        Integer val = parseNum(right);
	        if (val == null)
	          printLineErr(fileLine, fileName);
	        else
	          reg.ageYoung = reg.ageOld = val.intValue();
	      } else if (left.equals("lookGenM")) {
	        if (!right.equals("wantM"))
	          printLineErr(fileLine, fileName);
	        else
	          reg.lookM = true;
	      } else if (left.equals("lookGenF")) {
	        if (!right.equals("wantF"))
	          printLineErr(fileLine, fileName);
	        else
	          reg.lookW = true;
	      } else if (left.equals("lookGenGQ")) {
	        if (!right.equals("wantGQ"))
	          printLineErr(fileLine, fileName);
	        else
	          reg.lookGQ = true;
	      } else if (left.equals("minAge")) {
	        Integer val = parseNum(right);
	        if (val == null)
	          printLineErr(fileLine, fileName);
	        else
	          reg.minAge = val.intValue();
	      } else if (left.equals("maxAge")) {
	        Integer val = parseNum(right);
	        if (val == null)
	          printLineErr(fileLine, fileName);
	        else
	          reg.maxAge = val.intValue();
	      } else if (left.equals("lookGroup")) {
	        if (!right.equals("only") && !right.equals("yes") && !right.equals("no"))
	          printLineErr(fileLine, fileName);
	        else
	          reg.lookGroup = right;
	      } else if (left.equals("referred_by")) {
	        reg.referred = right.replaceAll( "\\W", " " );
	      } else if (left.equals("friends")) {
	         reg.friends = right.replaceAll("[^\\w ]", ".");
	      } else if (left.startsWith("location")) {
	        if (right.equals("fromEB"))
	          reg.fromEB = true;
	        else if (right.equals("fromSB"))
	          reg.fromSB = true;
	        else if (right.equals("fromNB"))
	          reg.fromNB = true;
	        else if (right.equals("fromSF"))
	          reg.fromSF = true;
	        else if (right.equals("fromSE"))
	          reg.fromSE = true;
	        else printLineErr(fileLine, fileName);
	      } else if (left.equals("first1")) {
	          reg.first1 = right;
	      } else if (left.equals("last1")) {
	        reg.last1 = right;
	      } else if (left.equals("first2")) {
	        reg.first2 = right;
	      } else if (left.equals("last2")) {
	        reg.last2 = right;
	      } else if (left.equals("first3")) {
	        reg.first3 = right;
	      } else if (left.equals("last3")) {
	        reg.last3 = right;
	      } else if (left.equals("first4")) {
	        reg.first4 = right;
	      } else if (left.equals("last4")) {
	        reg.last4 = right;
	      } else if (left.equals("ageYoungest")) {
	        Integer val = parseNum(right);
	        if (val == null)
	          printLineErr(fileLine, fileName);
	        else
	          reg.ageYoung = val.intValue();
	      } else if (left.equals("ageOldest")) {
	        Integer val = parseNum(right);
	        if (val == null)
	          printLineErr(fileLine, fileName);
	        else
	          reg.ageOld = val.intValue();
	      }

  }
	  
	


void parseGroupFile(ArrayList<String> fileLines, String fileName) {
    
    GroupReg reg = new GroupReg(true);
    
    for (int i = 0; i < fileLines.size(); i++) {
      String fileLine = fileLines.get(i);
      
      int indexOfColon = fileLine.indexOf(": ");
      if (indexOfColon < 0) {
        printLineErr(fileLine, fileName);
        continue;
      }
      
      String left = fileLine.substring(0, indexOfColon);
      String right = fileLine.substring(indexOfColon + 2);
      
      // we may have multiple lines for friends
      if (left.equals("friends")) {
        int offset = 0;
        while (fileLines.size() > i + offset + 1) {
          String next = fileLines.get(i + offset + 1);
          if (next.indexOf(": ") < 0) {
            right += " " + next;
            offset++;
          } else
            break;
        }
        i += offset;
      }
     
      processLine( reg, fileLine, fileName, left, right );
      
    }  
    
    // assemble output string
    
    try {
       
      outputGroupWriter.write(reg + "\n");
      
    } catch (IOException exc) {
      System.err.println("Output file IOException, bailing out: " + outputGroupFileName);
      exc.printStackTrace(System.err);
      outputError = true;
      return;
    }
  }
  
  Boolean parseBoolean(String boolString) {
    if (boolString.equals("yes") || boolString.equals("true") || boolString.equals("1"))
      return Boolean.TRUE;
    else if (boolString.equals("no") || boolString.equals("false") || boolString.equals("0"))
      return Boolean.FALSE;
    else return null;
  }
  
  Integer parseNum(String numString) {
    try {
      int num = Integer.valueOf(numString);
      return Integer.valueOf(num);
    } catch (NumberFormatException exc) {
      return null;
    }
  }
  
  String yesNo(boolean val) {
    return (val) ? "yes" : "no";
  }
  
  String yesBlank(boolean val) {
    return (val) ? "yes" : "";
  }
  
  void printLineErr(String line, String file) {
    System.err.println("Warning: unable to parse line '" + line + "' in file " + file);    
  }
  
  
  
  public class GroupReg {
	  
	  GroupReg( boolean group ) {
		  isGroup = group;
	  }
	  
	  public String toString() {
		  String outputString = first1 + "," + last1 + "," + first2 + "," + last2 + "," + first3 + "," + last3 + "," + first4 + "," + last4 + "," + pubname + "," + email + 
	  
      "," + yesNo(add_to_list) + "," + yesNo(man) + "," + yesNo(woman) + "," + 
      yesNo(GQ) + "," + ageYoung + "," + ageOld + "," + yesNo(lookM) + "," + yesNo(lookW) + "," + 
      yesNo(lookGQ) + "," + minAge + "," + maxAge + "," + lookGroup + "," + referred + "," + 
      friends + "," + yesBlank(fromEB) + "," + yesBlank(fromSB) + "," + 
      yesBlank(fromNB) + "," + yesBlank(fromSF) + "," + yesBlank(fromSE) + "\n";
	  return outputString;
	  }
	  
	  public boolean isGroup;
	  
    public String first1;
    public String last1;
    public String first2;
    public String last2;
    public String first3;
    public String last3;
    public String first4;
    public String last4;
    public String pubname;
    public String email;
    public boolean add_to_list;
    public boolean man;
    public boolean woman;
    public boolean GQ;
    public int ageYoung;
    public int ageOld;
    public boolean lookM;
    public boolean lookW;
    public boolean lookGQ;
    public int minAge;
    public int maxAge;
    public String lookGroup;
    public String referred;
    public String friends;
    public boolean fromEB;
    public boolean fromSB;
    public boolean fromNB;
    public boolean fromSF;
    public boolean fromSE;
  }
  
}
